package org.example.utils.post.post;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.annotation.LogTrace;
import org.example.dao.dao.post.PLikeDao;
import org.example.dao.dao.post.PPostDao;
import org.example.dao.dto.post.PostDto;
import org.example.dao.dto.post.PostRedisDto;
import org.example.dao.entity.post.PLike;
import org.example.dao.entity.post.PPost;
import org.example.exception.ParamLossErrorException;
import org.example.exception.ServerErrorException;
import org.example.skytool.commom.ContextUtil;
import org.example.skytool.commom.IdWorker;
import org.example.skytool.json.JackSonUtil;
import org.example.skytool.redis.CacheClient;
import org.example.skytool.redis.RedisUtils;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static org.example.skytool.redis.RedisConstants.*;

/**
 * @author 22449
 */
@Component
public class RedisPostService {

    @Resource
    private IdWorker idWorker;

    @Resource
    private ContextUtil contextUtil;
    @Resource
    private PLikeDao pLikeDao;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private PPostDao pPostDao;

    /**
     * 获取帖子内容缓存key
     */
    public String getPostContentKey(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_CONTENT_KIND;
        return header;
    }

    public String getPostContentLockKey(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_CONTENT_LOCK;
        return header;
    }


    /**
     * 获取帖子数据缓存key
     */
    public String getPostCacheHeader(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_DATA_KIND;
        return header;
    }

    public String getPostCacheLockKey(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_DATA_LOCK;
        return header;
    }

    /**
     * 获取帖子点赞列表key
     */
    public String getPostMemberHeader(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_MEMBER_KIND;
        return header;
    }

    public String getPostMemberLockHeader(String id) {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_CACHE_KIND + id + POST_POST_CACHE_MEMBER_LOCK;
        return header;
    }

    /**
     * 获取帖子历史记录key
     */
    public String getPostHistoryHeader(String id) {
        String header = USER_MOUDLE + id + USER_POST_CONTROLLER + USER_POST_HISTORY_KIND;
        return header;
    }

    /**
     * 获取搜索头部
     */
    public String getPostSearchRankHeader() {
        String header = POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_RANK_KIND +  POST_POST_RANK_COUNT_KIND
                + POST_POST_RANK_COUNT_SEARCH_KIND;
        return header;
    }

    /**
     * 获取热度帖子header
     */
    public String getTotalHeader() {
        return POST_MOUDLE + POST_POST_CONTROLLER +
                POST_POST_RANK_KIND + POST_POST_RANK_COUNT_KIND + POST_POST_RANK_COUNT_ACTIVE_KIND;
    }


    /**
     * 获取按时间顺序获取帖子的header
     */
    public String getCommomPostHeader(String themeId, String version) {
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_COMMOM_KIND + themeId
                + POST_POST_RANK_RANK_COMMOM_TOTAL_KIND + version;
    }

    public String getHotPostHeader(String themeId, String version) {
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_HOT_KIND + themeId
                + POST_POST_RANK_RANK_HOT_TOTAL_KIND + version;
    }

    public String getCommomPostVersionHeader(String themeId) {
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_COMMOM_KIND + themeId
                + POST_POST_RANK_RANK_COMMOM_TOTAL_VERSION_KIND;
    }

    public String getHotPostVersionHeader(String themeId) {
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_HOT_KIND + themeId
                + POST_POST_RANK_RANK_HOT_TOTAL_VERSION_KIND;
    }

    public String getRecommendPostVersionHeader(String themeId){
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_RECOMMEND_KIND + themeId
                + POST_POST_RANK_RANK_RECOMMEND_TOTAL_VERSION_KIND;
    }
    public String getRecommendPostHeader(String themeId,String version){
        return POST_MOUDLE + POST_POST_CONTROLLER + POST_POST_RANK_KIND +
                POST_POST_RANK_RANK_RECOMMEND_KIND + themeId
                + POST_POST_RANK_RANK_RECOMMEND_TOTAL_KIND +version;
    }


    @Resource
    private CacheClient cacheClient;


    //TODO 帖子基础

    public IPage<PostDto> getRecommendPost(String themeId,Integer start,Integer limit){
        String version = redisUtils.get(getRecommendPostVersionHeader(themeId));
        if (version == null) {
            throw new ServerErrorException("recommend post total version不存在");
        }
        start--;
        String key = getRecommendPostHeader(themeId, version);
        List<String> ids = redisUtils.listGet(key, start, limit);
        List<PostDto> list = selectPostDtoList(ids);
        IPage<PostDto> page = new Page<>();
        page.setRecords(list);
        page.setCurrent(start);
        page.setSize(limit);
        page.setTotal(redisUtils.listGetSize(key));
        page.setPages((long) Math.ceil(page.getTotal() / page.getSize()));
        return page;
    }

    /**
     * 热榜帖子
     */
    public IPage<PostDto> getHotPost(String themeId, Integer start, Integer limit) {
        String version = redisUtils.get(getHotPostVersionHeader(themeId));
        if (version == null) {
            throw new ServerErrorException("hot post total version不存在");
        }
        start--;
        String key = getHotPostHeader(themeId, version);
        List<String> ids = redisUtils.listGet(key, start, limit);
        List<PostDto> list = selectPostDtoList(ids);
        IPage<PostDto> page = new Page<>();
        page.setRecords(list);
        page.setCurrent(start);
        page.setSize(limit);
        page.setTotal(redisUtils.listGetSize(key));
        page.setPages((long) Math.ceil(page.getTotal() / page.getSize()));
        return page;
    }


    /**
     * 通过审核的时间优先帖子
     */
    public IPage<PostDto> getCommomPost(String themeId, Integer start, Integer limit) {
        String version = redisUtils.get(getCommomPostVersionHeader(themeId));
        if (version == null) {
            throw new ServerErrorException("commom post total version不存在");
        }
        start--;
        String key = getCommomPostHeader(themeId, version);
        List<String> ids = redisUtils.listGet(key, start, limit);
        List<PostDto> list = selectPostDtoList(ids);
        IPage<PostDto> page = new Page<>();
        page.setRecords(list);
        page.setCurrent(start);
        page.setSize(limit);
        page.setTotal(redisUtils.listGetSize(key));
        page.setPages((long) Math.ceil(page.getTotal() / page.getSize()));
        return page;
    }

    /**
     * 根据id获取帖子内容
     */
    public List<PostDto> selectPostDtoList(List<String> ids) {
        if (ids == null) {
            return null;
        }

        List<String> tempId = new ArrayList<>();
        for(int i = 0;i<ids.size();i++){
            tempId.add(getPostContentKey(ids.get(i)));
        }

        List<String> list = redisUtils.stringMutiGet(tempId);

        List<PostDto> postDtos = new ArrayList<>();
        if (list == null) {
            throw new ServerErrorException("获取帖子榜单异常");
        } else {
            for (int i = 0; i < ids.size(); i++) {
                PostDto postDto = null;
                //构造内容
                if (list.get(i) == null) {
                    postDto = getPostContent(ids.get(i));
                    postDtos.add(postDto);
                } else {
                    postDto = JackSonUtil.parse(PostDto.class, list.get(i));
                    postDtos.add(postDto);
                }
                //构造isLike
                Long userId = contextUtil.getId();
                postDto.setIsLike(judgeLike(postDto.getPPost().getId().toString(), userId.toString()));

                //增加浏览量
                browsePostCache(postDto.getPPost().getId().toString());

                //构造点赞信息
                PostRedisDto postRedisDto = getCacheInfo(postDto.getPPost().getId().toString());
                postRedisDto.setCacheData(postDto.getPPost());
            }
        }
        return postDtos;
    }


    /**
     * 获取帖子内容缓存
     */
    public PostDto getPostContent(String postId) {
        // 定义参数和回退函数
        String cacheKey = getPostContentKey(postId);
        Class type = PostDto.class;
        Map<String, Object> param = new HashMap<>();
        param.put("postId", postId);
        String lockKey = getPostContentLockKey(postId);

        Function<Map, PostDto> dbFallback = params -> {
            Long id = Long.valueOf((String) params.get("postId"));
            // 执行数据库回退逻辑，返回回退结果
            PostDto postDto = pPostDao.getPostDtoById(Long.valueOf(id));
            return postDto;
        };

        Long time = 30L;
        TimeUnit unit = TimeUnit.MINUTES;
        Long nullTtl = 30L;

        // 调用 query 方法
        PostDto result = cacheClient.queryString(cacheKey, type, param, lockKey, dbFallback, time, unit, nullTtl);
        return result;
    }

    /**
     * 删除帖子内容信息
     */
    public void delPostContent(String postId){
        String cacheKey = getPostContentKey(postId);
        redisUtils.del(cacheKey);
    }


    /**
     * 获取帖子点击 点赞 转发 浏览信息
     */
    @LogTrace
    public PostRedisDto getCacheInfo(String postId) {
        // 定义参数和回退函数
        String cacheKey = getPostCacheHeader(postId);
        Class type = PostRedisDto.class;
        Map<String, Object> param = new HashMap<>();
        param.put("postId", postId);
        String lockKey = getPostCacheLockKey(postId);
        Function<Map, PostRedisDto> dbFallback = params -> {
            Long id = Long.valueOf((String) params.get("postId"));
            // 执行数据库回退逻辑，返回回退结果
            PPost post = pPostDao.selectById(id);
            if (post == null) {
                return null;
            }
            PostRedisDto postRedisDto = new PostRedisDto(post);
            return postRedisDto;
        };
        Long time = 30L;
        TimeUnit unit = TimeUnit.MINUTES;
        Long nullTtl = 30L;
        PostRedisDto post = cacheClient.queryHash(cacheKey, type, param, lockKey, dbFallback, time, unit, nullTtl);
        return post;
    }


    /**
     * 构造帖子点赞成员
     */
    private void setPostLikeMember(String id) {
        // 定义参数和回退函数
        String cacheKey = getPostMemberHeader(id);
        Map<String, Object> param = new HashMap<>();
        param.put("postId", id);
        String lockKey = getPostMemberLockHeader(id);

        Function<Map, Long> dbFallback = params -> {

            Long postId = Long.valueOf((String) params.get("postId"));
            Long num = getLikeMemberInfo(postId.toString());
            return num;

        };
        Long time = 30L;
        TimeUnit unit = TimeUnit.MINUTES;
        Long nullTtl = 30L;

        cacheClient.hasSetKey(cacheKey,
                param, lockKey, dbFallback, time, unit, nullTtl
        );

    }

    /**
     * 获取帖子点赞成员
     */
    public Set<String> getPostLikeMember(String id){
        return redisUtils.setGetAll(getPostMemberLockHeader(id));
    }

    /**
     * 获取帖子点赞成员
     */
    private Long getLikeMemberInfo(String id) {

        LambdaQueryWrapper<PLike> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PLike::getLinkId, id);
        lambdaQueryWrapper.eq(PLike::getIsValid, 1);
        String key = getPostMemberHeader(id);
        long total = 0;
        long count = 0;
        long start = 1;
        long limit = 20;
        IPage<PLike> list = null;
        do {
            list = pLikeDao.selectPage(new Page<>(start, limit), lambdaQueryWrapper);
            if (total == 0) {
                total = list.getTotal();
            }

            if (list.getRecords().size() == 0) {

            } else {
                List<String> list1 = new ArrayList<>();
                for (PLike pLike : list.getRecords()) {
                    list1.add(pLike.getUserId().toString());
                }
                redisUtils.setAdd(key, list1);
            }

            if (count == total) {
                break;
            }
            start++;
            if (start * limit > total) {
                count = total;
            } else {
                count = start * limit;
            }
        } while (count <= total);

        return total;
    }


    /**
     * 判断是否点赞
     */
    public Boolean judgeLike(String id1, String id2) {
        setPostLikeMember(id1);
        String key = getPostMemberHeader(id1);
        if (redisUtils.setIsMember(key, id2)) {
            return true;
        }
        return false;
    }


    /**
     * 点赞帖子
     *
     * @return
     */
    public Long likePostCache(String id) {
        Long userId = contextUtil.getId();

        if(getCacheInfo(id)==null){
            throw new ParamLossErrorException("帖子不存在" + id);
        }

        String memberPrefix = getPostMemberHeader(id);
        String cachePrefix = getPostCacheHeader(id);
        Long likeNum = 0L;


        //定时任务更新数据
        if (judgeLike(id, userId.toString())) {
            likeNum = redisUtils.hashIncrBy(cachePrefix, POST_POST_CACHE_DATA_LIKE_KIND, -1);
            redisUtils.setRemove(memberPrefix, userId.toString());
            //刷新过期时间
            redisUtils.expire(cachePrefix, 30, TimeUnit.MINUTES);
            redisUtils.expire(memberPrefix, 30, TimeUnit.MINUTES);
            //添加active
            increActive(id,-5.0);
        } else {
            likeNum = redisUtils.hashIncrBy(cachePrefix, POST_POST_CACHE_DATA_LIKE_KIND, 1);
            redisUtils.setAdd(memberPrefix, userId.toString());
            redisUtils.expire(cachePrefix, 30, TimeUnit.MINUTES);
            redisUtils.expire(memberPrefix, 30, TimeUnit.MINUTES);
            //添加active
            increActive(id,5.0);
        }

        return likeNum;
    }

    /**
     * 点击帖子
     */
    public Long clickPostCache(String id) {
        if(getCacheInfo(id)==null){
            throw new ParamLossErrorException("帖子不存在" + id);
        }
        Long num = redisUtils.hashIncrBy(getPostCacheHeader(id), POST_POST_CACHE_DATA_CLICK_KIND, 1);
        redisUtils.expire(getPostCacheHeader(id), 30, TimeUnit.MINUTES);
        //添加active
        increActive(id,3.0);
        return num;
    }

    /**
     * 浏览帖子
     */
    public Long browsePostCache(String id) {
        if(getCacheInfo(id)==null){
            throw new ParamLossErrorException("帖子不存在" + id);
        }
        Long num = redisUtils.hashIncrBy(getPostCacheHeader(id), POST_POST_CACHE_DATA_BROWSE_KIND, 1);
        redisUtils.expire(getPostCacheHeader(id), 30, TimeUnit.MINUTES);
        //添加active
        increActive(id,1.0);
        return num;
    }

    /**
     * 转发帖子
     */
    public Long transmitPostCache(String id) {
        if(getCacheInfo(id)==null){
            throw new ParamLossErrorException("帖子不存在" + id);
        }
        Long num = redisUtils.hashIncrBy(getPostCacheHeader(id), POST_POST_CACHE_DATA_TRANSMIT_KIND, 1);
        redisUtils.expire(getPostCacheHeader(id), 30, TimeUnit.MINUTES);

        //添加active
        increActive(id,10.0);
        return num;
    }





    //TODO 搜索

    /**
     * 添加历史记录缓存
     */
    public void addPostHistory(String content) {
        addSearchHot(content);

        Long userId = contextUtil.getId();
        String key = getPostHistoryHeader(userId.toString());
        redisUtils.zSetAdd(key, content, idWorker.nextId());
    }

    /**
     * 获取历史记录缓存
     */
    public List<String> getPostHistory() {
        Long userId = contextUtil.getId();
        String key = getPostHistoryHeader(userId.toString());
        return redisUtils.zSetGetRange(key, 1, 10);
    }

    /**
     * 清除历史记录缓存
     */
    public void removePostHistory() {
        Long userId = contextUtil.getId();
        String key = getPostHistoryHeader(userId.toString());
        redisUtils.del(key);
    }



    /**
     * 获取搜索热榜
     */
    public List<String> getSearchHot(Integer start, Integer limit) {
        String key = getPostSearchRankHeader();
        return redisUtils.zSetGetRange(key, start, limit);
    }

    /**
     * 获取搜索热榜前10
     */
    public List<String> getSearchHotTop10() {
        return getSearchHot(1, 10);
    }

    /**
     * 为搜索添加热度
     */
    public void addSearchHot(String content) {
        String key = getPostSearchRankHeader();
        redisUtils.zSetIncrey(key, content, 1.0);
    }


    //TODO 热度

    public void addActive(String id, Integer score) {
        redisUtils.zSetAdd(getTotalHeader(), id, score);
    }

    public void increActive(String id, Double score) {
        redisUtils.zSetIncrey(getTotalHeader(), id, score);
    }

    public void clearTotalActive() {
        redisUtils.del(getTotalHeader());
    }

    public Set<ZSetOperations.TypedTuple<String>> getTotalActive() {
        return redisUtils.zSetGetAll(getTotalHeader());
    }

    public List<String> getTotalActive(Integer start, Integer size) {
        return redisUtils.zSetGetRange(getTotalHeader(), start-1, size);
    }


}
